// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <pollux/expression/function_signature.h>
#include <pollux/expression/signature_binder.h>
#include <pollux/type/type.h>

namespace kumo::pollux::exec {
    /// Bind a function signature with a concrete return type. bindings() return a
    /// map from each type variable in the signature to the corresponding concrete
    /// type if determined, or a nullptr if the type variable cannot be determined
    /// by the return type.
    class ReverseSignatureBinder : private SignatureBinderBase {
    public:
        ReverseSignatureBinder(
            const exec::FunctionSignature &signature,
            const TypePtr &returnType)
            : SignatureBinderBase{signature}, returnType_{returnType} {
        }

        /// Try bind returnType_ to the return type of the function signature. Return
        /// true if the binding succeeds, or false otherwise.
        bool tryBind();

        /// Return the determined bindings. This function should be called after
        /// tryBind() and only if tryBind() returns true. If a type variable is not
        /// determined by tryBind(), it maps to a nullptr.
        const melon::F14FastMap<std::string, TypePtr> &bindings() const {
            POLLUX_CHECK(
                tryBindSucceeded_, "tryBind() must be called first and succeed");
            return typeVariablesBindings_;
        }

        /// Return the integer bindings produced by 'tryBind'. This function should be
        /// called after 'tryBind' and only if 'tryBind' returns true.
        const melon::F14FastMap<std::string, int> &integerBindings() const {
            POLLUX_CHECK(
                tryBindSucceeded_, "tryBind() must be called first and succeed");
            return integerVariablesBindings_;
        }

    private:
        const TypePtr returnType_;

        // True if 'tryBind' has been called and succeeded. False otherwise.
        bool tryBindSucceeded_{false};
    };
} // namespace kumo::pollux::exec
